/****************************************************************************/
/* File: scs-color.h                                                        */
/* Desc: Implementation file for the graphics color type                    */
/* Created: 2014-11-11                                                      */
/* Revised: 2014-11-12                                                      */
/* Authors: Sterling P. de Mille                                            */
/****************************************************************************/

// specification file
#include "scs-color.h"

SCScolor::SCScolor ()
{
	m_afValues[RED] = 0;
	m_afValues[GREEN] = 0;
	m_afValues[BLUE] = 0;
	m_afValues[ALPHA] = 1;
}

SCScolor::SCScolor (const SCScolor& rkColor)
{
	m_afValues[RED] = rkColor.m_afValues[RED];
	m_afValues[GREEN] = rkColor.m_afValues[GREEN];
	m_afValues[BLUE] = rkColor.m_afValues[BLUE];
	m_afValues[ALPHA] = rkColor.m_afValues[ALPHA];
}

SCScolor::SCScolor (const SCSfloat fShade, const SCSfloat fAlpha)
{
	m_afValues[RED] = fShade;
	m_afValues[GREEN] = fShade;
	m_afValues[BLUE] = fShade;
	m_afValues[ALPHA] = fAlpha;
}

SCScolor::SCScolor (const SCSfloat fRed, const SCSfloat fGreen, const SCSfloat fBlue, const SCSfloat fAlpha)
{
	m_afValues[RED] = fRed;
	m_afValues[GREEN] = fGreen;
	m_afValues[BLUE] = fBlue;
	m_afValues[ALPHA] = fAlpha;
}

SCScolor::~SCScolor ()
{
	m_afValues[RED] = 0;
	m_afValues[GREEN] = 0;
	m_afValues[BLUE] = 0;
	m_afValues[ALPHA] = 0;
}

SCSfloat SCScolor::GetRed () const
{
	return m_afValues[RED];
}

SCSfloat SCScolor::GetGreen () const
{
	return m_afValues[GREEN];
}

SCSfloat SCScolor::GetBlue () const
{
	return m_afValues[BLUE];
}

SCSfloat SCScolor::GetAlpha () const
{
	return m_afValues[ALPHA];
}

SCSint8u SCScolor::GetColor2222 () const
{
	SCSint8u iR, iG, iB, iA;
	iR = ((SCSint8u)((0x3 * GetRed()) + 0.5f) & 0x3);
	iG = ((SCSint8u)((0x3 * GetGreen()) + 0.5f) & 0x3);
	iB = ((SCSint8u)((0x3 * GetBlue()) + 0.5f) & 0x3);
	iA = ((SCSint8u)((0x3 * GetAlpha()) + 0.5f) & 0x3);

	return ((iR << 6) | (iG << 4) | (iB << 2) | iA);
}

SCSint16u SCScolor::GetColor565 () const
{
	SCSint16u iR, iG, iB;
	iR = ((SCSint16u)((0x1f * GetRed()) + 0.5f) & 0x1f);
	iG = ((SCSint16u)((0x3f * GetGreen()) + 0.5f) & 0x3f);
	iB = ((SCSint16u)((0x1f * GetBlue()) + 0.5f) & 0x1f);

	return ((iR << 11) | (iG << 5) | iB);
}

SCSint32u SCScolor::GetColor8888 () const
{
	SCSint32u iR, iG, iB, iA;
	iR = ((SCSint32u)((0xff * GetRed()) + 0.5f) & 0xff);
	iG = ((SCSint32u)((0xff * GetGreen()) + 0.5f) & 0xff);
	iB = ((SCSint32u)((0xff * GetBlue()) + 0.5f) & 0xff);
	iA = ((SCSint32u)((0xff * GetAlpha()) + 0.5f) & 0xff);

	return ((iR << 24) | (iG << 16) | (iB << 8) | iA);
}

SCSfloat SCScolor::GetMin () const
{
	if ((m_afValues[RED] <= m_afValues[GREEN]) && (m_afValues[RED] <= m_afValues[BLUE])) return m_afValues[RED];
	if ((m_afValues[GREEN] <= m_afValues[BLUE]) && (m_afValues[GREEN] <= m_afValues[RED])) return m_afValues[GREEN];
	if ((m_afValues[BLUE] <= m_afValues[RED]) && (m_afValues[BLUE] <= m_afValues[GREEN])) return m_afValues[BLUE];

	return m_afValues[RED];
}

SCSfloat SCScolor::GetMax () const
{
	if ((m_afValues[RED] >= m_afValues[GREEN]) && (m_afValues[RED] >= m_afValues[BLUE])) return m_afValues[RED];
	if ((m_afValues[GREEN] >= m_afValues[BLUE]) && (m_afValues[GREEN] >= m_afValues[RED])) return m_afValues[GREEN];
	if ((m_afValues[BLUE] >= m_afValues[RED]) && (m_afValues[BLUE] >= m_afValues[GREEN])) return m_afValues[BLUE];

	return m_afValues[RED];
}

void SCScolor::SetRed (const SCSfloat fRed)
{
	m_afValues[RED] = fRed;
}

void SCScolor::SetGreen (const SCSfloat fGreen)
{
	m_afValues[GREEN] = fGreen;
}

void SCScolor::SetBlue (const SCSfloat fBlue)
{
	m_afValues[BLUE] = fBlue;
}

void SCScolor::SetAlpha (const SCSfloat fAlpha)
{
	m_afValues[ALPHA] = fAlpha;
}

SCSfloat SCScolor::GetChroma () const
{
	return (GetMax() - GetMin());
}

SCSfloat SCScolor::GetHue () const
{
	SCSfloat fMax = GetMax();
	SCSfloat fChroma = GetChroma();

	SCSfloat fHprime = 0;
	if (fMax == m_afValues[RED]) {
		fHprime = ((m_afValues[GREEN] - m_afValues[BLUE]) / fChroma);
		fHprime = (fHprime - (6 * (SCSint)(fHprime / 6)));
	} else if (fMax == m_afValues[GREEN]) {
		fHprime = ((m_afValues[BLUE] - m_afValues[RED]) / fChroma) + 2;
	} else if (fMax == m_afValues[BLUE]) {
		fHprime = ((m_afValues[RED] - m_afValues[GREEN]) / fChroma) + 4;
	}
	return (fHprime * 60);
}

SCSfloat SCScolor::GetValue () const
{
	return GetMax();
}

SCSfloat SCScolor::GetLightness () const
{
	return (0.5f * (GetMin() + GetMax()));
}

SCSfloat SCScolor::GetLuma () const
{
	return ((0.3f * m_afValues[RED]) + (0.59f * m_afValues[GREEN]) + (0.11f * m_afValues[BLUE]));
}

SCSfloat SCScolor::GetGrayscale () const
{
	return GetLuma();
}

SCSfloat SCScolor::GetSaturationHSV () const
{
	SCSfloat fValue = GetValue();
	if (fValue != 0) {
		return (GetChroma() / fValue);
	}
	return 0;
}

SCSfloat SCScolor::GetSaturationHSL () const
{
	SCSfloat fLightness = GetLightness();
	if ((0 < fLightness) && (fLightness < 1)) {
		return (GetChroma() / (1 - fabs((2.0 * fLightness) - 1)));
	}
	return 0;
}

SCSfloat SCScolor::GetTransparency () const
{
	return m_afValues[ALPHA];
}

void SCScolor::SetHue (const SCSfloat fHue)
{
	SetHSV(fHue, GetSaturationHSV(), GetValue());
}

void SCScolor::SetSaturation (const SCSfloat fSaturation)
{
	SetHSV(GetHue(), fSaturation, GetValue());
}

void SCScolor::SetValue (SCSfloat fValue)
{
	SetHSV(GetHue(), GetSaturationHSV(), fValue);
}

void SCScolor::SetLightness (SCSfloat fLightness)
{
	SetHSL(GetHue(), GetSaturationHSL(), fLightness);
}

void SCScolor::SetTransparency (SCSfloat fTransparency)
{
	m_afValues[ALPHA] = fTransparency;
}

void SCScolor::SetColor (const SCScolor& rkColor) {
	m_afValues[RED] = rkColor.m_afValues[RED];
	m_afValues[GREEN] = rkColor.m_afValues[GREEN];
	m_afValues[BLUE] = rkColor.m_afValues[BLUE];
	m_afValues[ALPHA] = rkColor.m_afValues[ALPHA];
}

void SCScolor::SetShade (const SCSfloat fShade, const SCSfloat fAlpha)
{
	m_afValues[RED] = fShade;
	m_afValues[GREEN] = fShade;
	m_afValues[BLUE] = fShade;
	m_afValues[ALPHA] = fAlpha;

}

void SCScolor::SetRGB (const SCSfloat fRed, const SCSfloat fGreen, const SCSfloat fBlue, const SCSfloat fAlpha)
{
	m_afValues[RED] = fRed;
	m_afValues[GREEN] = fGreen;
	m_afValues[BLUE] = fBlue;
	m_afValues[ALPHA] = fAlpha;
}

SCScolor SCScolor::Add(const SCScolor& rkColor) const
{
	return SCScolor(this->GetRed() + rkColor.GetRed(), this->GetGreen() + rkColor.GetGreen(), this->GetBlue() + rkColor.GetBlue(), this->GetTransparency());
}

SCScolor SCScolor::GetPrimeColor (const SCSfloat fChroma, const SCSfloat fHue) const
{
	SCSfloat fHprime = (fHue / 60.0f);
	fHprime = (fHprime - (2 * (SCSint)(fHprime / 2)));
	SCSfloat fX = (fChroma * (1 - fabs(fHprime - 1)));
	SCSfloat fR1, fG1, fB1;

	if ((0 <= fHprime) && (fHprime < 1)) {
		fR1 = fChroma;
		fG1 = fX;
		fB1 = 0;
	} else if ((1 <= fHprime) && (fHprime < 2)) {
		fR1 = fX;
		fG1 = fChroma;
		fB1 = 0;
	} else if ((2 <= fHprime) && (fHprime < 3)) {
		fR1 = 0;
		fG1 = fChroma;
		fB1 = fX;
	} else if ((3 <= fHprime) && (fHprime < 4)) {
		fR1 = 0;
		fG1 = fX;
		fB1 = fChroma;
	} else if ((4 <= fHprime) && (fHprime < 5)) {
		fR1 = fX;
		fG1 = 0;
		fB1 = fChroma;
	} else if ((5 <= fHprime) && (fHprime < 6)) {
		fR1 = fChroma;
		fG1 = 0;
		fB1 = fX;
	}
	return SCScolor(fR1, fG1, fB1);
}

void SCScolor::SetHSV (const SCSfloat fHue, const SCSfloat fSaturation, const SCSfloat fValue, const SCSfloat fTransparency)
{
	SCSfloat fChroma = (fSaturation * fValue);
	SCScolor kPrime = GetPrimeColor(fChroma, fHue);
	SCSfloat fMin = (fValue - fChroma);
	SCScolor kBase = SCScolor(fMin, GetTransparency());

	SetColor(kBase.Add(kPrime));
}

void SCScolor::SetHSL (const SCSfloat fHue, const SCSfloat fSaturation, const SCSfloat fLightness, const SCSfloat fTransparency)
{
	SCSfloat fChroma = (fSaturation * (1 - fabs((2.0 * fLightness) - 1)));
	SCScolor kPrime = GetPrimeColor(fChroma, fHue);
	SCSfloat fMin = (fLightness - (0.5f * fChroma));
	SCScolor kBase = SCScolor(fMin, GetTransparency());

	SetColor(kBase.Add(kPrime));
}

void SCScolor::SetHCL (const SCSfloat fHue, const SCSfloat fChroma, const SCSfloat fLuma, const SCSfloat fTransparency)
{
	SCScolor kPrime = GetPrimeColor(fChroma, fHue);
	SCScolor kBase = SCScolor(fLuma, GetTransparency());

	SetColor(kBase.Add(kPrime));
}
